/////////////////////////////////////////////////////////////////////////////
// Name:        fileutils.cpp
// Purpose:     General-purpose file utility classes and functions
// Author:      Julian Smart
// Modified by:
// Created:     2006-02-09
// RCS-ID:      $Id: anthemionutils.cpp,v 1.19 2008/01/25 14:25:28 anthemion Exp $
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "wx/utils.h"
#include "wx/file.h"
#include "wx/ffile.h"
#include "wx/log.h"
#include "wx/confbase.h"
#include "wx/tokenzr.h"
#include "wx/stdpaths.h"
#include "wx/wfstream.h"
#include "wx/dir.h"
#include "wx/docview.h"
#include "wx/mdi.h"
#include "wx/filename.h"
#include "wx/notebook.h"
#include "wx/filefn.h"
#include "wx/zipstrm.h"
#include "wx/zstream.h"
#include "wx/tarstrm.h"
#include "wx/progdlg.h"

#include "fileutils/fileutils.h"

#ifdef __WXMSW__
#include <windows.h>
#include "wx/msw/winundef.h"
#include "wx/msw/uxtheme.h"
#include "wx/msw/private.h"
#endif

#ifdef __WXMAC__
#include "wx/mac/private.h"
#ifndef __DARWIN__
#include "InternetConfig.h"
#endif
#endif

#ifdef __UNIX__
// For access
#include <unistd.h>
#include  <sys/stat.h>
#endif

// Are the two files identical in content?
bool FileUtilities::FilesEqual(const wxString& filename1, const wxString& filename2)
{
    wxFile file1, file2;

    {
        wxLogNull logNull;
        if (!file1.Open(filename1))
            return false;
        
        if (!file2.Open(filename2))
            return false;
    }

    size_t size1 = (size_t) file1.Length();
    size_t size2 = (size_t) file2.Length();

    if (size1 != size2)
        return false;

    static char buf1[1024];
    static char buf2[1024];
    size_t bytesLeft = size1;

    while (bytesLeft > 0)
    {
        size_t bytesToRead = wxMin((size_t) 1024, bytesLeft);

        file1.Read((void*) buf1, bytesToRead);
        file2.Read((void*) buf2, bytesToRead);

        if (strncmp(buf1, buf2, bytesToRead) != 0)
            return false;

        bytesLeft -= bytesToRead;
    }

    return true;
}

// Rename first file to second file, copying and deleting original file if
// the rename didn't work.
bool FileUtilities::RenameOrCopyFile(const wxString& filename1, const wxString& filename2)
{
    if (wxFileExists(filename2))
        wxRemoveFile(filename2);

    if (!wxRenameFile(filename1, filename2))
    {
        if (!CopyFile(filename1, filename2))
        {
            wxString msg;
            msg.Printf(_("Could not rename %s to %s."), (const wxChar*) filename1, (const wxChar*) filename2);
            wxLogError(msg);
            return false;
        }
        else
            wxRemoveFile(filename1);
    }

    return true;
}

bool FileUtilities::SplitPath(const wxString& fullpath,
                           wxString *pstrPath,
                           wxString *pstrName,
                           wxString *pstrExt,
                           wxPathFormat format)
{
    wxFileName::SplitPath(fullpath, pstrPath, pstrName, pstrExt, format);

    if (pstrExt)
    {
        if (pstrExt->Find(wxT("\"")) != wxNOT_FOUND ||
            pstrExt->Find(wxT(" ")) != wxNOT_FOUND ||
            pstrExt->Find(wxT("-")) != wxNOT_FOUND)
        {
            *pstrExt = wxEmptyString;
        }
    }

    return true;
}

// Get temporary directory
wxString FileUtilities::GetTempDirectory()
{

    wxString dir;
#if defined(__WXMAC__) && !defined(__DARWIN__)
    dir = wxMacFindFolder(  (short) kOnSystemDisk, kTemporaryFolderType, kCreateFolder ) ;
#elif defined(__WXMSW__)
    if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
    {
        wxLogLastError(_T("GetTempPath"));
    }
    else
    {
        if (dir.Length() > 1 && dir.Last() == wxT('\\'))
        {
            dir = dir.Left(dir.Length() - 1);
        }

        // Make sure it's in long-name form
#ifdef __WXMSW__
        wxFileName dirFilename(dir, wxEmptyString);
        dirFilename.Normalize(wxPATH_NORM_LONG);
        dir = dirFilename.GetPath();
#endif
    }
#else
    dir = wxGetenv(_T("TMP"));
    if ( dir.empty() )
    {
        dir = wxGetenv(_T("TEMP"));
    }
#endif
    if ( dir.empty() )
    {
        // default
#ifdef __DOS__
        dir = _T(".");
#else
        dir = _T("/tmp");
#endif
    }
    return dir;
}

// Get suitable download directory
wxString FileUtilities::GetDownloadDirectory()
{
    // TODO
    return GetDocumentDirectory();
}

// Get a suitable directory for putting documents by default
wxString FileUtilities::GetDocumentDirectory()
{
    wxString docDir;
#ifdef __WXMSW__
    wxStandardPaths stdPaths;
    docDir = stdPaths.GetDocumentsDir();
    if (!docDir.IsEmpty())
        return docDir;

    docDir = wxGetenv(wxT("USERPROFILE"));
    if (!docDir.IsEmpty())
    {
        wxString d(docDir + wxT("\\Documents"));
        if (wxDirExists(d))
            docDir = d;
        else
        {
            d = docDir + wxT("\\My Documents");
            if (wxDirExists(d))
                docDir = d;
        }
    }
#endif

#if defined(__UNIX__) && !defined(__WXMAC__)
    if (docDir.IsEmpty() && !wxDirExists(wxGetHomeDir() + wxT("/My Documents")) && !wxDirExists(wxGetHomeDir() + wxT("/Documents")))
    {
        wxStandardPaths stdPaths;
        wxString stdDocDir = stdPaths.GetDocumentsDir();
        if (stdDocDir != wxGetHomeDir())
            docDir = stdDocDir;
    }
#endif

    if (docDir.IsEmpty())
    {
        docDir = wxGetHomeDir() + wxT("/My Documents");
        if (DirExists(docDir))
        {
            // Do nothing
        }
        else
        {
            docDir = wxGetHomeDir() + wxT("/Documents");
            if (DirExists(docDir))
            {
                // Do nothing
            }
            else
            {
                if (!DirExists(docDir)) // use DirExists in case it's a symbol link
                    if (!wxMkdir(docDir))
                        docDir = wxGetHomeDir();
            }
        }
    }
    return docDir;
}

// Does an extra test in case there's a symbolic link that looks like a file
bool FileUtilities::DirExists(const wxString& str)
{
#ifdef __WXMAC__
    return wxDirExists(str) || wxFileExists(str);
#else
    return wxDirExists(str);
#endif
}

// Get the application data directory for the current user
wxString FileUtilities::GetUserAppDataDirectory(const wxString& appName)
{
    wxUnusedVar(appName);

    wxStandardPaths paths;
    wxString dataDir = paths.GetUserDataDir();
    if (dataDir.IsEmpty())
        dataDir = GetDocumentDirectory();

#if defined(__WXMAC__)
    dataDir = wxMacFindFolder(  (short) kUserDomain, kApplicationSupportFolderType, kDontCreateFolder ) ;
    dataDir += wxTheApp->GetAppName();
#elif defined(__UNIX__)
    // Avoid conflict with the dotfile used for general config data
    dataDir += wxT("Data");
    dataDir.Replace(wxT(" "), wxT(""));
    dataDir.Replace(wxT("'"), wxT(""));
#endif

    if (!wxDirExists(dataDir))
        wxMkdir(dataDir);

    return dataDir;
}

// Change the Unix file mode
bool FileUtilities::Chmod(const wxString& path, int mode, bool addMode)
{
#ifdef __UNIX__
    int m = mode;
    if (addMode)
    {
        m |= GetFileMode(path);
    }
    if ( chmod( (const char*) path.fn_str(), mode) == -1 )
    {
        wxLogSysError(_("Failed to set file permission for %s"), path.c_str());
        return false;
    }
#else
    wxUnusedVar(path);
    wxUnusedVar(mode);
    wxUnusedVar(addMode);
#endif
    return true;
}

// Find the given file under the a path and change its mode
bool FileUtilities::FindAndChmod(const wxString& searchPath, const wxString& filename, int mode, bool addMode)
{
    bool success = false;
    wxArrayString files;
    wxDir::GetAllFiles(searchPath, & files);
    size_t i;
    for (i = 0; i < files.GetCount(); i++)
    {
        wxString fullPath(files[i]);
        // wxPrintf(wxT("%s\n"), fullPath.c_str());
        if (wxFileNameFromPath(fullPath) == filename && wxFileExists(fullPath))
        {
            if (Chmod(fullPath, mode, addMode))
                success = true;
        }
    }
    return success;
}


// Get the Unix file mode. If path is empty, get the default mode for a new file.
int FileUtilities::GetFileMode(const wxString& path)
{
#ifdef __UNIX__
    // the temp file should have the same permissions as the original one
    mode_t mode;

    wxStructStat st;
    if ( !path.IsEmpty() && stat( (const char*) path.fn_str(), &st) == 0 )
    {
        mode = st.st_mode;
    }
    else
    {
        // file probably didn't exist, just give it the default mode _using_
        // user's umask (new files creation should respect umask)
        mode_t mask = umask(0777);
        mode = 0666 & ~mask;
        umask(mask); // sets the default create mode for the current process. See http://en.wikipedia.org/wiki/Umask
    }

    return mode;

#else
    wxUnusedVar(path);

    return 0;
#endif
}


// Make sure the folder exists, otherwise create recursively.
bool FileUtilities::MkDirRecursively(const wxString& path)
{
    if (wxDirExists(path))
        return true;

    wxString p;

    wxStringTokenizer tkz(path, wxT("/\\"));
    p = tkz.GetNextToken();

    while (tkz.HasMoreTokens())
    {
        wxString token = tkz.GetNextToken();

        p += wxFILE_SEP_PATH + token;
        if (!wxDirExists(p))
        {
            if (!wxMkdir(p))
                return false;
        }
    }
    return true;
}

// Remove dir recursively
bool FileUtilities::RemoveDirRecursively(const wxString& path)
{
    // First get any directories, and recursively remove them.
    wxArrayString dirs;
        
    {
        wxString d;
        wxDir dir;
        if (!dir.Open(path))
            return false;
        
        bool success = dir.GetFirst(& d, wxT("*"), wxDIR_DIRS);
        while (success)
        {
            wxString s = FileUtilities::AppendPaths(path, d);
            dirs.Add(s);
            
            success = dir.GetNext(& d);
        }
    }

    size_t i;
    for (i = 0; i < dirs.GetCount(); i++)
    {
        wxString d = dirs[i];
        if (wxDirExists(d) && d != wxT(".") && d != wxT(".."))
        {
            RemoveDirRecursively(d);
        }
    }

    wxArrayString paths;
    wxDir::GetAllFiles(path, & paths, wxEmptyString, wxDIR_FILES);
    for (i = 0; i < paths.GetCount(); i++)
    {
        wxString p = paths[i];
        wxRemoveFile(p);
    }

    wxRmdir(path);

    return true;
}

// Already catered for by wxDir::FindAllFiles

#if 0
// Find matching file(s) recursively
bool FileUtilities::FindMatchingFiles(const wxString& path, const wxString& spec, wxArrayString& matchingFiles)
{
    // First get any directories, and recursively remove them.
    wxArrayString dirs;
        
    {
        wxString d;
        wxDir dir;
        if (!dir.Open(path))
            return false;
        
        bool success = dir.GetFirst(& d, wxT("*"), wxDIR_DIRS);
        while (success)
        {
            wxString s = FileUtilities::AppendPaths(path, d);
            dirs.Add(s);
            
            success = dir.GetNext(& d);
        }
    }

    size_t i;
    for (i = 0; i < dirs.GetCount(); i++)
    {
        wxString d = dirs[i];
        if (wxDirExists(d) && d != wxT(".") && d != wxT(".."))
        {
            FindMatchingFiles(d, spec, matchingFiles);
        }
    }

    wxArrayString paths;
    wxDir::GetAllFiles(path, & paths, spec, wxDIR_FILES);
    for (i = 0; i < paths.GetCount(); i++)
    {
        wxString p = paths[i];
        matchingFiles.Add(p);
    }

    return true;
}
#endif

// Remove trailing separator
bool FileUtilities::RemoveTrailingSeparator(wxString& filename)
{
    if (filename.Length() > 0 && (filename[filename.Length()-1] == wxT('/') || filename[filename.Length()-1] == wxT('\\')))
    {
        filename = filename.Mid(0, filename.Length()-1);
    }
    return true;
}

// Does the filename have this extension?
bool FileUtilities::HasExtension(const wxString& filename, const wxString& ext)
{
    wxString file, name, ext2;
    wxSplitPath(filename, & file, & name, & ext2);
    return (ext2.Lower() == ext.Lower());
}

// Gets the extension
wxString FileUtilities::GetExtension(const wxString& filename)
{
    wxString file, name, ext;
    wxSplitPath(filename, & file, & name, & ext);
    return ext;
}

// Ensure extension is present
void FileUtilities::EnsureExtension(wxString& path, const wxString& requiredExt)
{
    if (path.IsEmpty())
        return;

    wxString ext = GetExtension(path);
    ext.MakeLower();
    if (ext.IsEmpty() || ext != requiredExt)
    {
        StripExtension(path);
        path += wxT(".");
        path += requiredExt;
    }
}

// Intelligent strip extension: don't strip before a separator,
// and don't strip if the 'extension' is different from the passed extension
// that we expect - can be comma-separated list.
bool FileUtilities::StripExtension(wxString& path, const wxString& validExtensions)
{
    wxString p, name, ext;
    wxSplitPath(path, &p, &name, &ext);

    if (!validExtensions.IsEmpty() && !ext.IsEmpty() && validExtensions.Find(ext) == wxNOT_FOUND)
        return false;
    else if (!ext.IsEmpty())
    {
        path = path.Left(path.Length() - ext.Length() - 1);
        return true;
    }
    else
        return false;
}


#if defined(__WXMSW__) && !defined(__WXWINCE__)
#include <shlobj.h>

// CreateLink - uses the Shell's IShellLink and IPersistFile interfaces 
//              to create and store a shortcut to the specified object. 
//
// Returns the result of calling the member functions of the interfaces. 
//
// Parameters:
// lpszPathObj  - address of a buffer containing the path of the object. 
// lpszPathLink - address of a buffer containing the path where the 
//                Shell link is to be stored. 
// lpszDesc     - address of a buffer containing the description of the 
//                Shell link. 

bool FileUtilities::CreateShortcut(const wxString& path, const wxString& pathLink, const wxString descr) 
{ 
    HRESULT hres; 
    IShellLink* psl; 
 
    // Get a pointer to the IShellLink interface. 
    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, 
                            IID_IShellLink, (LPVOID*)&psl); 
    if (SUCCEEDED(hres)) 
    { 
        IPersistFile* ppf; 
 
        // Set the path to the shortcut target and add the description. 
        psl->SetPath(path); 
        psl->SetDescription(descr); 
 
        // Query IShellLink for the IPersistFile interface for saving the 
        // shortcut in persistent storage. 
        hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); 
 
        if (SUCCEEDED(hres)) 
        { 
            WCHAR wsz[MAX_PATH]; 
 
            // Ensure that the string is Unicode. 
            MultiByteToWideChar(CP_ACP, 0, pathLink.mb_str(), -1, wsz, MAX_PATH); 
			
            // Add code here to check return value from MultiByteWideChar 
            // for success.
 
            // Save the link by calling IPersistFile::Save. 
            hres = ppf->Save(wsz, TRUE); 
            ppf->Release(); 
        } 
        psl->Release(); 
    } 
    return SUCCEEDED(hres); 
}

/*
http://codeguru.earthweb.com/shell/gettingshortcuts.shtml

I saw Michael Taupitz's idea of creating Links (shortcuts) and I
realized that there are many times when we stumble across a link
in command prompt mode and we have no idea what this .lnk file points to.

Here's a function I wrote which returns the Target and Arguments of a .lnk
file in a wxString. Hope you find it helpful.
*/

wxString FileUtilities::GetShortcutTarget(const wxString& LinkFileName)
{
	HRESULT hres;
	
	wxString Link, Temp = LinkFileName;
	Temp.MakeLower();
	if (Temp.Find(wxT(".lnk"))==-1)           //Check if the name ends with .lnk
		Link = LinkFileName + wxT(".lnk");   //if not, append it
	else
		Link = LinkFileName;

	wxString Info;
	Info.Empty();
	
	IShellLink* psl;

	//Create the ShellLink object
	hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
				IID_IShellLink, (LPVOID*) &psl);
	
	if (SUCCEEDED(hres))
	{
		IPersistFile* ppf;
		//Bind the ShellLink object to the Persistent File
		hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
		if (SUCCEEDED(hres))
		{
			WORD wsz[MAX_PATH];
			//Get a UNICODE wide string wsz from the Link path
			MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Link.mb_str(), -1, wsz,
                MAX_PATH);
			
			//Read the link into the persistent file
			hres = ppf->Load(wsz, 0);
			
			if (SUCCEEDED(hres))
			{
				//Read the target information from the link object
				//UNC paths are supported (SLGP_UNCPRIORITY)

                wxChar buf[1024];
				psl->GetPath(buf, 1024, NULL, 				             
                    SLGP_UNCPRIORITY);
				Info = wxString(buf);

				//Read the arguments from the link object
				psl->GetArguments(buf, 1024);
                wxString args(buf);
                if (!args.IsEmpty())
                {
				    Info += wxT(" ");
                    Info += args;
                }
			}
		}
	}
	psl->Release();
	//Return the Target and the Argument as a wxString
	return Info;
}
#endif

// Is read-only?
bool FileUtilities::IsReadOnly(const wxString& filename)
{
    if (!wxFileExists(filename))
        return FALSE;

#ifdef __WXWINCE__
    DWORD attr = GetFileAttributes(filename);
    return (attr & FILE_ATTRIBUTE_READONLY) != 0;
#else
    int result = wxAccess(filename, 02);
    return (result != 0);
#endif
}

// Is this an absolute path? Works for filenames using
// foreign OS conventions.
bool FileUtilities::IsAbsolutePath(const wxString& path)
{
    if (path.IsEmpty())
       return false;

    wxString p(path);
#ifdef __UNIX__
    // Make a DOS absolute path into a Unix-ish path
    if (p[1u] == wxT(':'))
    {
        p = wxT("/") + p.Mid(2);
    }
    p.Replace(wxT("\\"), wxT("/"));
#endif
#ifdef __WXMSW__
    p.Replace(wxT("/"), wxT("\\"));
#endif
    return wxIsAbsolutePath(p);
}

// Makes an absolute path relative if is under relativeToPath
wxString FileUtilities::MakeRelative(const wxString& path, const wxString& relativeToPath)
{
    if (path.IsEmpty())
        return path;

    wxString p(path);
#ifdef __UNIX__
    p.Replace(wxT("\\"), wxT("/"));
#elif defined(__WXMSW__)
    p.Replace(wxT("/"), wxT("\\"));
#endif
    if (!IsAbsolutePath(p))
        return p;

    wxString relativeToPath2(relativeToPath);
#ifdef __UNIX__
    relativeToPath2.Replace(wxT("\\"), wxT("/"));
#elif defined(__WXMSW__)
    relativeToPath2.Replace(wxT("/"), wxT("\\"));
#endif

    size_t pathLen = p.Length();
    size_t relativeToPathLen = relativeToPath2.Length();
    if (relativeToPathLen < pathLen &&
#ifdef __WXMSW__
        p.Left(relativeToPathLen).CmpNoCase(relativeToPath) == 0
#else
        p.Left(relativeToPathLen) == relativeToPath
#endif
        )
    {
        wxString str = p.Mid(relativeToPathLen);
        if (!str.IsEmpty() && str[0u] == wxFILE_SEP_PATH)
            str = str.Mid(1);
        return str;
    }
    return p;
}

// Makes a relative path absolute
wxString FileUtilities::MakeAbsolute(const wxString& path, const wxString& appPath)
{
    wxString p(path);
#ifdef __UNIX__
    p.Replace(wxT("\\"), wxT("/"));
#elif defined(__WXMSW__)
    p.Replace(wxT("/"), wxT("\\"));
#endif

    if (p.IsEmpty())
        return p;

    if (IsAbsolutePath(p))
        return p;
    return appPath + wxFILE_SEP_PATH + p;
}

// Explores the given folder, under Windows only
bool FileUtilities::ExploreFolder(const wxString& folder)
{
#ifdef __WXMSW__
    if (folder != wxEmptyString)
    {
        wxExecute(wxString(_T("explorer.exe \"")) + folder + wxT("\""));
    }

#elif defined(__WXMAC__)
    wxString cmd(wxT("open \""));
    cmd += folder;
    cmd += wxT("\"");

    wxExecute(cmd);
#else
    wxArrayString fileManagers;

    wxString customFileManager(wxGetenv(_T("FILEMANAGER")));
    if (!customFileManager.IsEmpty())
        fileManagers.Add(customFileManager);

    fileManagers.Add(wxT("dolphin"));               // KDE 4
    fileManagers.Add(wxT("nautilus"));              // Gnome
    fileManagers.Add(wxT("XandrosFileManager"));    // Asus Eee PC
    fileManagers.Add(wxT("konqueror"));             // KDE 3
    fileManagers.Add(wxT("thunar"));                // Xfce/Linpus

    wxString p;

    size_t i;
    for (i = 0; i < fileManagers.GetCount(); i++)
    {
        p = FindInPath(fileManagers[i]);
        if (!p.IsEmpty())
            break;
    }

    if (p.IsEmpty())
    {
        wxMessageBox(_("Cannot find a file manager to use - please set the FILEMANAGER environment variable."), _("Explore Folder"), wxOK|wxICON_EXCLAMATION);
        return false;
    }

    wxExecute(p + wxT(" \"") + folder + wxT("\""));
#endif
    return true;
}

// Append paths without duplicating separator
wxString FileUtilities::AppendPaths(const wxString& p1, const wxString& p2, const wxString& sep)
{
    wxString path = p1;

    if (p1.Last() != wxT('\\') && p1.Last() != wxT('/') && p2[0] != wxT('\\') && p2[0] != wxT('/'))
        path += sep;

    path += p2;

    return path;
}

// Does the drive exist?
bool FileUtilities::DriveExists(const wxString& drive)
{
    wxString d(drive);
#ifdef __WXMSW__
    if (drive.Last() != wxT('\\') && drive.Last() != wxT('/'))
        d += wxT("\\");
#endif
    return wxDirExists(d);
}

#ifdef __WXGTK__
// Get only the paths under path, and don't recurse
static void FileUtilitiesGetDirs(const wxString& path, wxArrayString& paths)
{
    wxDir dir;
    if (dir.Open(path))
    {
        wxString filename;
        bool cont = dir.GetFirst(& filename, wxT("*"), wxDIR_DIRS);
        while (cont)
        {
            wxString p = path + wxFILE_SEP_PATH + filename;
            // wxPrintf(wxT("%s\n"), p.c_str());
            paths.Add(p);
            cont = dir.GetNext(& filename);
        }
    }
}
#endif

// Get available drives, optionally only the removable ones
bool FileUtilities::GetAvailableDrives(wxArrayString& paths, wxArrayString& names, bool removableOnly)
{
#ifdef __WXGTK__
    wxArrayString p;

    // Enumerate drives in mount directory
    wxDir dir;
    if (wxDirExists(wxT("/media")))
        FileUtilitiesGetDirs(wxT("/media"), p);

    if (wxDirExists(wxT("/mnt")))
        FileUtilitiesGetDirs(wxT("/mnt"), p);

    names.Clear();
    size_t i;
    for (i = 0; i < p.GetCount(); i++)
    {
        // Filter out non-removeable drives
        if (!removableOnly || (/* p[i].Find(wxT("floppy")) == wxNOT_FOUND && */ p[i].Find(wxT("cdrom")) == wxNOT_FOUND))
        {
            paths.Add(p[i]);
            names.Add(p[i]);
        }
    }
#else
    // From dirctrlg.cpp
    extern size_t wxGetAvailableDrives(wxArrayString &paths, wxArrayString &names, wxArrayInt &icon_ids);

    wxArrayString paths1, names1;
    wxArrayInt iconIds;

    wxGetAvailableDrives(paths1, names1, iconIds);

    size_t i;
    for (i = 0; i < paths1.GetCount(); i++)
    {
#ifdef __WXMSW__
        if (!removableOnly || (::GetDriveType(paths1[i]) == DRIVE_REMOVABLE))
#endif
#ifdef __WXMAC__
        // Don't include root path if we're just looking at removeable drives
        if (!removableOnly || (paths1[i] != wxT("/")))
#endif
        {
            paths.Add(paths1[i]);
            names.Add(names1[i]);
        }
    }
#endif
    return true;
}

// Try to get a volume (drive) from the filename, return true if successful.
bool FileUtilities::GetVolumeFromFilename(const wxString& filename, wxString& volume)
{
#ifdef __WXMSW__
    volume = wxFileName(filename).GetVolume();

    if (volume.Length() == 1)
        volume += wxT(":");

    return !volume.IsEmpty();
#else
    // TODO: what to do in the case of Linux and Mac?
    // See: http://gentoo-wiki.com/HOWTO_USB_Mass_Storage_Device

    // Try matching first part of the filename 

    wxArrayString drives, names;
    GetAvailableDrives(drives, names);

    // Find the largest volume that matches
    wxString lastVol;

    size_t i;
    for (i = 0; i < drives.GetCount(); i++)
    {
        wxString substr(filename.Left(drives[i].Length()));
        if (substr != wxT("/") && substr == drives[i])
        {
            if (lastVol.IsEmpty() || substr.Length() > lastVol.Length())
            {
                volume = drives[i];
                lastVol = volume;
            }
        }
    }
    return volume.Length() > 0;
#endif
}

// Split volume and filename from path. Returns false if we can't (e.g. wasn't a removeable volume)
bool FileUtilities::SplitVolume(const wxString& path, wxString& filename, wxString& volume)
{
#ifdef __WXMSW__
    wxFileName::SplitVolume(path, & volume, & filename);
    if (volume.Length() == 1)
        volume += wxT(":");
    return true;
#else
    if (GetVolumeFromFilename(path, volume))
    {
        filename = path.Mid(volume.Length());
        if (filename.Length() > 0 && filename[0] == wxT('/'))
            filename = filename.Mid(1);
        return true;
    }
    else
        return false;
#endif
}

// Find the file substituting this volume. Tests existence first.
bool FileUtilities::GetFilenameSubstitutingVolume(const wxString& filename, wxString& newFilename, const wxString& newVolume, const wxString& oldVolume,
                                                           bool isFilename)
{
    // wxPrintf(wxT("GetFilenameSubstitutingVolume: file = %s, newVolume = %s, oldVolume = %s\n"), filename.c_str(), newVolume.c_str(), oldVolume.c_str());
    if (filename.IsEmpty() || !IsAbsolutePath(filename))
    {
        // wxPrintf(wxT("Filename was empty or not absolute path.\n"));
        return false;
    }

    if (!oldVolume.IsEmpty() && !newVolume.IsEmpty())
    {
#if 1 // def __WXMSW__
        if (oldVolume.Length() != 1 /* don't accept "/" */ && filename.Left(oldVolume.Length()).Lower() == oldVolume.Lower())
#else
        if (oldVolume.Length() != 1 /* don't accept "/" */ && filename.Left(oldVolume.Length()) == oldVolume)
#endif
        {
            wxString fname = filename.Mid(oldVolume.Length());
            if (fname[0] == wxT('\\') || fname[0] == wxT('/'))
                fname = fname.Mid(1);

            newFilename = AppendPaths(newVolume, fname);
#ifdef __WXMSW__
            newFilename.Replace(wxT("/"), wxT("\\"));
#else
            newFilename.Replace(wxT("\\"), wxT("/"));
#endif
            if ((isFilename && wxFileExists(newFilename)) || (!isFilename && wxDirExists(newFilename)))
            {
                // wxPrintf(wxT("Matched - %s\n"), newFilename.c_str());
                return true;
            }
            else
            {
                // wxPrintf(wxT("Did not match - %s\n"), newFilename.c_str());
                return false;
            }
        }
    }

#if 0
    //  Fallback, e.g. if we don't have an old volume
    wxString filenameWithoutVolume, existingVolume;
    if (!newVolume.IsEmpty() && SplitVolume(filename, filenameWithoutVolume, existingVolume))
    {
        newFilename = AppendPaths(newVolume, filenameWithoutVolume);
#ifdef __WXMSW__
        newFilename.Replace(wxT("/"), wxT("\\"));
#else
        newFilename.Replace(wxT("\\"), wxT("/"));
#endif
        if (wxFileExists(newFilename))
            return true;
    }
#endif

    return false;
}

// Load file history with volume substitution if file not found
void FileUtilities::FileHistoryLoadWithVolumeSubstitution(wxFileHistory& fileHistory, wxConfigBase& config, const wxString& newVolume, const wxString& oldVolume)
{
    wxArrayString files;

    int i = 0;
    wxString buf;
    buf.Printf(wxT("file%d"), (int)i+1);
    wxString historyFile;
    while ((i < fileHistory.GetMaxFiles()) && config.Read(buf, &historyFile) && (!historyFile.empty()))
    {
        if (!wxFileExists(historyFile))
        {
            wxString filename2;
            if (GetFilenameSubstitutingVolume(historyFile, filename2, newVolume, oldVolume))
            {
                // fileHistory.AddFileToHistory(filename2);
                files.Insert(filename2, 0);

                // Make sure the new filename is reflected in the config file, so it matches
                // the external drive we will save on exit.
                config.Write(buf, filename2);
            }
        }
        else
            files.Insert(historyFile, 0);
            // fileHistory.AddFileToHistory(historyFile);

        i ++;
        buf.Printf(wxT("file%d"), (int)i+1);
        historyFile = wxEmptyString;
    }

    for (i = 0; i < (int) files.GetCount(); i++)
    {
        wxString f(files[i]);
        fileHistory.AddFileToHistory(f);
    }
}

/// File/stream utility functions

// Copy a fixed size of data from an input stream to an output stream,
// using a buffer to speed it up.
bool FileUtilities::BufferedCopy(wxInputStream& inStream, wxOutputStream& outStream, size_t size)
{
    static unsigned char buf[1024];
    size_t bytesLeft = size;

    while (bytesLeft > 0)
    {
        size_t bytesToRead = wxMin((size_t) 1024, bytesLeft);

        inStream.Read((void*) buf, bytesToRead);
        outStream.Write((void*) buf, bytesToRead);

        bytesLeft -= bytesToRead;
    }

    return TRUE;
}

// Copy data from an input stream to an output stream, using a buffer to speed it up.
// This version doesn't need a size.
bool FileUtilities::BufferedCopy(wxInputStream& inStream, wxOutputStream& outStream)
{
    static unsigned char buf[1024];

    while (!inStream.Eof())
    {
        inStream.Read((void*) buf, 1024);
        int lastRead = inStream.LastRead();
        if (lastRead > 0)
            outStream.Write((void*) buf, lastRead);
    }

    return TRUE;
}

// Copy file to stream
bool FileUtilities::CopyFileToStream(wxOutputStream& stream, const wxString& filename)
{
    wxFile file;
    if (!file.Open(filename))
        return FALSE;

    size_t size = (size_t) file.Length();
    file.Close();

    wxFileInputStream inStream(filename);
    if (!inStream.Ok())
        return FALSE;

    return BufferedCopy(inStream, stream, size);
}

// Copy stream to new file
bool FileUtilities::CopyStreamToFile(wxInputStream& stream, const wxString& filename, size_t size)
{
    wxFileOutputStream outStream(filename);
    if (!outStream.Ok())
        return FALSE;

    return BufferedCopy(stream, outStream, size);
}

// translate the filenames before passing them to OS functions
#define OS_FILENAME(s) (s.fn_str())

// Replacement for wxWidgets function that fails if can't set permissions
bool FileUtilities::CopyFile(const wxString& file1, const wxString& file2, int flags)
{
    bool overwrite = (flags & FILEUTILS_COPYFILE_OVERWRITE) != 0;
    bool ignorePermissions = (flags & FILEUTILS_COPYFILE_IGNORE_PERMISSIONS) != 0;
    wxUnusedVar(ignorePermissions);

#if defined(__WIN32__) && !defined(__WXMICROWIN__)
    // CopyFile() copies file attributes and modification time too, so use it
    // instead of our code if available
    //
    // NB: 3rd parameter is bFailIfExists i.e. the inverse of overwrite
    if ( !::CopyFile(file1, file2, !overwrite) )
    {
        wxLogSysError(_("Failed to copy the file '%s' to '%s'"),
                      file1.c_str(), file2.c_str());

        return false;
    }
#elif defined(__OS2__)
    if ( ::DosCopy((PSZ)file1.c_str(), (PSZ)file2.c_str(), overwrite ? DCPY_EXISTING : 0) != 0 )
        return false;
#elif defined(__PALMOS__)
    // TODO with http://www.palmos.com/dev/support/docs/protein_books/Memory_Databases_Files/
    return false;
#elif wxUSE_FILE // !Win32

    wxStructStat fbuf;

    // get permissions of file1
    bool permissionsSucceeded = (wxStat( file1.c_str(), &fbuf) == 0);

    if ( !ignorePermissions && !permissionsSucceeded)
    {
        // the file probably doesn't exist or we haven't the rights to read
        // from it anyhow
        wxLogSysError(_("Impossible to get permissions for file '%s'"),
                      file1.c_str());
        return false;
    }

    // open file1 for reading
    wxFile fileIn(file1, wxFile::read);
    if ( !fileIn.IsOpened() )
        return false;

    // remove file2, if it exists. This is needed for creating
    // file2 with the correct permissions in the next step
    if ( wxFileExists(file2)  && (!overwrite || !wxRemoveFile(file2)))
    {
        wxLogSysError(_("Impossible to overwrite the file '%s'"),
                      file2.c_str());
        return false;
    }

    // reset the umask as we want to create the file with exactly the same
    // permissions as the original one
    wxCHANGE_UMASK(0);

    // create file2 with the same permissions than file1 and open it for
    // writing

    wxFile fileOut;
    if ( !fileOut.Create(file2, overwrite, fbuf.st_mode & 0777) )
        return false;

    // copy contents of file1 to file2
    char buf[4096];
    size_t count;
    for ( ;; )
    {
        count = fileIn.Read(buf, WXSIZEOF(buf));
        if ( fileIn.Error() )
            return false;

        // end of file?
        if ( !count )
            break;

        if ( fileOut.Write(buf, count) < count )
            return false;
    }

    // we can expect fileIn to be closed successfully, but we should ensure
    // that fileOut was closed as some write errors (disk full) might not be
    // detected before doing this
    if ( !fileIn.Close() || !fileOut.Close() )
        return false;

#if !defined(__VISAGECPP__) && !defined(__WXMAC__) || defined(__UNIX__)
    // no chmod in VA.  Should be some permission API for HPFS386 partitions
    // however
    bool changedPermissions = false;
    if (permissionsSucceeded)
        changedPermissions = (chmod(OS_FILENAME(file2), fbuf.st_mode) == 0 );

    if (!changedPermissions && !ignorePermissions)
    {
        wxLogSysError(_("Impossible to set permissions for the file '%s'"),
            file2.c_str());
        return false;
    }
#endif // OS/2 || Mac

#else // !Win32 && ! wxUSE_FILE

    // impossible to simulate with wxWidgets API
    wxUnusedVar(file1);
    wxUnusedVar(file2);
    wxUnusedVar(overwrite);
    return false;

#endif // __WXMSW__ && __WIN32__

    return true;
}

// Copy files in sourceDir to destDir
bool FileUtilities::CopyDir(const wxString& sourceDir, const wxString& destDir, bool ifNotExists, const wxString& wildcard, int dirFlags)
{
    if (!wxDirExists(destDir))
        wxMkdir(destDir);

    bool success = true;

    if (wxDirExists(sourceDir))
    {
        wxDir dir;

        // First just get the files
        wxArrayString sourceFiles;
        dir.GetAllFiles(sourceDir, & sourceFiles, wildcard, dirFlags & ~wxDIR_DIRS);

        size_t i;
        for (i = 0; i < sourceFiles.GetCount(); i++)
        {
            wxString sourceFilename = sourceFiles[i];
            wxString destFilename = destDir + wxFILE_SEP_PATH + wxFileNameFromPath(sourceFilename);

            if (!ifNotExists || !wxFileExists(destFilename))
            {
                if (dirFlags & wxDIR_DIRS)
                {
                    // Make dest dir if necessary
                    wxString p(wxPathOnly(destFilename));
                    if (!wxDirExists(p))
                        MkDirRecursively(p);
                }

                if (!CopyFile(sourceFilename, destFilename))
                    success = false;
            }
        }

        // Now recurse
        if (dirFlags & wxDIR_DIRS)
        {
            wxArrayString sourceDirs;
            wxDir dirs;

            wxString dirName;
            if (dirs.Open(sourceDir))
            {
                bool cont = dirs.GetFirst(&dirName, wxT("*"), dirFlags & ~wxDIR_FILES);
                while ( cont )
                {
                    sourceDirs.Add(dirName);

                    cont = dirs.GetNext(&dirName);
                }
            }
            
            for (i = 0; i < sourceDirs.GetCount(); i++)
            {
                wxString newSourceDir = sourceDir + wxFILE_SEP_PATH + sourceDirs[i];
                wxString newDestDir = destDir + wxFILE_SEP_PATH + sourceDirs[i];

                success = CopyDir(newSourceDir, newDestDir, ifNotExists, wildcard, dirFlags);
            }            
        }
    }

    return success;
}

// Copy temp filename to target filename prompting if the rename/copy failed.
// If backupFilename was non-empty, it will first be deleted before the copy if it
// exists, and the old targetFilename will be copied to backupFilename before
// targetFilename is overwritten.
bool FileUtilities::SafelyCopyFile(const wxString& tempFilename, const wxString& targetFilename, const wxString& backupFilename, bool leaveBackup, wxWindow* parent)
{
    // Remove the old .bak file
    if (!backupFilename.IsEmpty() && wxFileExists(backupFilename))
        wxRemoveFile(backupFilename);
    
    // Copy the old file to the .bak.
    // Don't prompt about backing up even if an error occurs: it's not
    // too important and it may confuse the user.

    if (leaveBackup)
    {
        if (wxFileExists(targetFilename))
        {
            if (!wxRenameFile(targetFilename, backupFilename))
            {
                CopyFile(targetFilename, backupFilename);
                wxRemoveFile(targetFilename);
            }
        }
    }
    else
    {
        wxRemoveFile(targetFilename);
    }
    
    // Finally, copy the temporary file to the proper filename
    if (!wxRenameFile(tempFilename, targetFilename))
    {
        while (true)
        {
            if (CopyFile(tempFilename, targetFilename))
            {
                wxRemoveFile(tempFilename);
                return true;
            }
            else
            {
                wxString msg(wxString::Format(_("Sorry, could not write to %s.\nTry again?"), targetFilename.c_str()));
                wxString title(_("File Saving"));
                if (!parent)
                    parent = wxTheApp->GetTopWindow();                

                if (wxNO == wxMessageBox(msg, title, wxYES_NO|wxICON_QUESTION, parent))
                    return false;
            }
        }
    }
    
    return true;    
}

// Create stream, prompting for a retry, and if failing, returning false.
// If it returns false, there is no need for the app to delete the stream:
// it will have been deleted.
bool FileUtilities::SafelyCreateFileOutputStream(wxFileOutputStream** stream, const wxString& filename, wxWindow* parent, const wxString& msg, const wxString& title)
{
    wxString msg1(msg);
    wxString title1(title);

    if (msg1.IsEmpty())
        msg1 = _("Sorry, could not write to %s.\nTry again?");
    if (title1.IsEmpty())
        title1 = _("File Saving Problem");

    if (!parent)
        parent = wxTheApp->GetTopWindow();

    while (true)
    {
        wxLogNull logNull;
        *stream = new wxFileOutputStream(filename);
        if ((*stream)->Ok())
            return true;

        delete (*stream);
        (*stream) = NULL;

        if (wxNO == wxMessageBox(wxString::Format(msg1, filename.c_str()), title1, wxYES_NO|wxICON_QUESTION, parent))
            return false;
    }

    return false;
}

// Create file, prompting for a retry, and if failing, returning false.
// If it returns false, there is no need for the app to delete the file:
// it will have been deleted.
bool FileUtilities::SafelyCreateFile(wxFFile** file, const wxString& filename, wxWindow* parent, const wxString& msg, const wxString& title)
{
    wxString msg1(msg);
    wxString title1(title);

    if (msg1.IsEmpty())
        msg1 = _("Sorry, could not write to %s.\nTry again?");
    if (title1.IsEmpty())
        title1 = _("File Saving Problem");

    if (!parent)
        parent = wxTheApp->GetTopWindow();

    while (true)
    {
        *file = new wxFFile(filename, wxT("w"));
        if ((*file)->IsOpened())
            return true;

        delete (*file);
        (*file) = NULL;

        if (wxNO == wxMessageBox(wxString::Format(msg1, filename.c_str()), title1, wxYES_NO|wxICON_QUESTION, parent))
            return false;
    }

    return false;
}

// Create file, prompting for a retry, and if failing, returning false.
// If it returns false, there is no need for the app to delete the file:
// it will have been deleted.
bool FileUtilities::SafelyCreateFile(wxFile** file, const wxString& filename, wxWindow* parent, const wxString& msg, const wxString& title)
{
    wxString msg1(msg);
    wxString title1(title);

    if (msg1.IsEmpty())
        msg1 = _("Sorry, could not write to %s.\nTry again?");
    if (title1.IsEmpty())
        title1 = _("File Saving Problem");

    if (!parent)
        parent = wxTheApp->GetTopWindow();

    while (true)
    {
        *file = new wxFile(filename, wxFile::write);
        if ((*file)->IsOpened())
            return true;

        delete (*file);
        (*file) = NULL;

        if (wxNO == wxMessageBox(wxString::Format(msg1, filename.c_str()), title1, wxYES_NO|wxICON_QUESTION, parent))
            return false;
    }

    return false;
}

// Find the encoding of an HTML file
wxString FileUtilities::FindHtmlEncoding(const wxString& filename)
{
    wxFFile file(filename);
    if (file.IsOpened())
    {
        const int len = 400;
        char buf[len+1];
        buf[file.Read(buf, len)] = 0;

        wxString str = wxString::FromAscii(buf);
        wxString encoding;
        wxString toFind(wxT("charset="));

        int pos = str.Find(toFind);
        if (pos != -1)
        {
            size_t i = pos + toFind.Length();
            while (i < str.Length() && str[i] != wxT('"') && str[i] != wxT(' ') && str[i] != wxT(';'))
            {
                encoding += str[i];
                i ++;
            }
            return encoding;
        }

    }
    return wxEmptyString;
}

// Convert to a valid filename (assumes no path separators in filename)
wxString FileUtilities::MakeValidFilename(const wxString& filename, bool allowSpaces)
{
    wxString str(filename);
    str.Replace(wxT("\\"), wxEmptyString);
    str.Replace(wxT("/"), wxEmptyString);
    str.Replace(wxT("/"), wxEmptyString);
    if (!allowSpaces)
        str.Replace(wxT(" "), wxEmptyString);
    str.Replace(wxT("\""), wxEmptyString);
    str.Replace(wxT("'"), wxEmptyString);
    str.Replace(wxT(","), wxEmptyString);
    str.Replace(wxT("("), wxEmptyString);
    str.Replace(wxT(")"), wxEmptyString);
    str.Replace(wxT("*"), wxEmptyString);
    str.Replace(wxT("?"), wxEmptyString);
    str.Replace(wxT("%"), wxEmptyString);
    str.Replace(wxT("$"), wxEmptyString);
    str.Replace(wxT("!"), wxEmptyString);
    str.Replace(wxT("{"), wxEmptyString);
    str.Replace(wxT("}"), wxEmptyString);
    str.Replace(wxT("["), wxEmptyString);
    str.Replace(wxT("]"), wxEmptyString);
    str.Replace(wxT("+"), wxEmptyString);
    str.Replace(wxT("~"), wxEmptyString);
    str.Replace(wxT("#"), wxEmptyString);
    str.Replace(wxT(":"), wxEmptyString);
    str.Replace(wxT(";"), wxEmptyString);
    str.Replace(wxT("@"), wxEmptyString);
    str.Replace(wxT("<"), wxEmptyString);
    str.Replace(wxT(">"), wxEmptyString);
    str.Replace(wxT("|"), wxEmptyString);
    str.Replace(wxT("&"), wxEmptyString);
    str.Replace(wxT("^"), wxEmptyString);
    str.Replace(wxT("`"), wxEmptyString);
    str.Replace(wxT("^"), wxEmptyString);

    return str;
}

// Interpret command line as an array
wxArrayString FileUtilities::SplitCommand(const wxString& command)
{
    wxArrayString args;

    if (command.empty())
        return args;

    int argc = 0;
    wxString argument;
    const wxChar *cptr = command.c_str();
    wxChar quotechar = wxT('\0'); // is arg quoted?
    bool escaped = false;

    // split the command line in arguments
    do
    {
        argument = wxEmptyString;
        quotechar = wxT('\0');

        // eat leading whitespace:
        while ( wxIsspace(*cptr) )
            cptr++;

        if ( *cptr == wxT('\'') || *cptr == wxT('"') )
            quotechar = *cptr++;

        do
        {
            if ( *cptr == wxT('\\') && ! escaped )
            {
                escaped = true;
                cptr++;
                continue;
            }

            // all other characters:
            argument += *cptr++;
            escaped = false;

            // have we reached the end of the argument?
            if ( (*cptr == quotechar && ! escaped)
                 || (quotechar == wxT('\0') && wxIsspace(*cptr))
                 || *cptr == wxT('\0') )
            {
                args.Add(argument);
                argc++;

                // if not at end of buffer, swallow last character:
                if(*cptr)
                    cptr++;

                break; // done with this one, start over
            }
        } while(*cptr);
    } while(*cptr);

    return args;
}    

// Find file/command on path, or empty string if not found
wxString FileUtilities::FindInPath(const wxString& cmd)
{
    wxPathList pathList;
    pathList.AddEnvList(wxT("PATH"));
    wxString fullPath = pathList.FindValidPath(cmd);
    return fullPath;
}

/*!
 * Archive utilities
 */

// Get the zip entry for the given filename
wxZipEntry* ArchiveUtilities::FindEntry(wxZipInputStream& stream, const wxString& filename)
{
    wxZipEntry* zipEntry = NULL;
    while (true)
    {
        if (zipEntry)
        {
            delete zipEntry;
        }
        zipEntry = stream.GetNextEntry();
        if (!zipEntry)
            break;
        else
        {
            wxString zipFile = zipEntry->GetInternalName();
            if (zipFile == filename)
                return zipEntry;
        }
    }
    return NULL;
}

// Copy a fixed size of data from an input stream to an output stream,
// using a buffer to speed it up.
bool ArchiveUtilities::BufferedCopy(wxInputStream& inStream, wxOutputStream& outStream, size_t size)
{
    return FileUtilities::BufferedCopy(inStream, outStream, size);
}

// Copy stream to new file
bool ArchiveUtilities::CopyStreamToFile(wxInputStream& stream, const wxString& filename, size_t size)
{
    return FileUtilities::CopyStreamToFile(stream, filename, size);
}

// Copy entry to new file
bool ArchiveUtilities::CopyEntryToFile(wxZipInputStream& stream, const wxString& filename, wxZipEntry* entry)
{
    stream.OpenEntry(*entry);

    return CopyStreamToFile(stream, filename, stream.GetSize());
}

// Unarchive to the given folder
bool ArchiveUtilities::UnzipTo(const wxString& archive, const wxString& destinationFolder, wxProgressDialog* progressDialog)
{
    int i = 0;
    wxFFileInputStream inputStr(archive);
    if (inputStr.Ok())
    {
        wxZipInputStream zipStr(inputStr);

        wxZipEntry* zipEntry = NULL;
        while (true)
        {
            if (zipEntry)
            {
                delete zipEntry;
            }
            zipEntry = zipStr.GetNextEntry();
            if (!zipEntry)
                break;
            else if (!zipEntry->IsDir())
            {
                wxString zipFile = zipEntry->GetInternalName();

                if (progressDialog)
                {
                    wxString msg(wxString::Format(_("Writing %s"), zipFile.c_str()));
                    if (!progressDialog->Update(i, msg))
                    {
                        // Aborting on user request
                        delete zipEntry;
                        return false;
                    }
                }

                // Extract to destination

                wxString destFilename = destinationFolder + wxFILE_SEP_PATH + zipFile;
                zipStr.OpenEntry(*zipEntry);

                // TODO: for Unix, will need to make binaries executable.
                // May also be permission issues.

                wxString path = wxPathOnly(destFilename);
                if (!wxDirExists(path))
                    FileUtilities::MkDirRecursively(path);

                if (!CopyStreamToFile(zipStr, destFilename, zipStr.GetSize()))
                {
                    delete zipEntry;
                    return false;
                }

            }
            i ++;
        }
    }

    return true;
}

// Get the number of entries in the zip file
int ArchiveUtilities::GetZipEntryCount(const wxString& filename)
{
    wxFFileInputStream inputStr(filename);
    if (inputStr.Ok())
    {
        wxZipInputStream zipStr(inputStr);
        return zipStr.GetTotalEntries();
    }
    else
        return 0;
}

// Unarchive to the given folder, optionally notifying the progress dialog
bool ArchiveUtilities::GUnzipTo(const wxString& archive, const wxString& destinationFile)
{
    wxFFileInputStream inputStream(archive);
    wxZlibInputStream zStream(inputStream);
    wxFFileOutputStream outputStream(destinationFile);

    return FileUtilities::BufferedCopy(zStream, outputStream);
}

// Unarchive to the given folder, optionally notifying the progress dialog
bool ArchiveUtilities::UntarTo(const wxString& archive, const wxString& destinationFolder, wxProgressDialog* progressDialog)
{
    int i = 0;
    wxFFileInputStream inputStr(archive);
    if (inputStr.Ok())
    {
        wxTarInputStream tarStr(inputStr);

        wxTarEntry* tarEntry = NULL;
        while (true)
        {
            if (tarEntry)
            {
                delete tarEntry;
            }
            tarEntry = tarStr.GetNextEntry();
            if (!tarEntry)
                break;
            else
            {
                if (tarEntry->GetTypeFlag() == wxTAR_REGTYPE)
                {
                    wxString tarFile = tarEntry->GetInternalName();


                    if (progressDialog)
                    {
                        wxString msg(wxString::Format(_("Writing %s"), tarFile.c_str()));
                        if (!progressDialog->Update(i, msg))
                        {
                            // Aborting on user request
                            delete tarEntry;
                            return false;
                        }
                    }

                    // Extract to destination

                    wxString destFilename = destinationFolder + wxFILE_SEP_PATH + tarFile;
                    tarStr.OpenEntry(*tarEntry);

                    wxString path = wxPathOnly(destFilename);
                    if (!wxDirExists(path))
                        FileUtilities::MkDirRecursively(path);

                    if (!CopyStreamToFile(tarStr, destFilename, tarStr.GetSize()))
                    {
                        delete tarEntry;
                        return false;
                    }
                    else
                    {
#ifdef __UNIX__
                        FileUtilities::Chmod(destFilename, tarEntry->GetMode());
#endif
                    }
                }

                i ++;
            }
        }
    }

    return true;
}

// Copy to the given folder, optionally notifying the progress dialog.
// relativeTo is the folder to be substracted from each file to determine the relative path for the destination file.
bool ArchiveUtilities::CopyTo(const wxString& relativeTo, const wxArrayString& files, const wxString& destinationFolder, wxProgressDialog* progressDialog)
{
    size_t i;
    for (i = 0; i < files.GetCount(); i++)
    {
        wxString sourceFile = files[i];

        wxFileName fname(sourceFile);
        fname.MakeRelativeTo(relativeTo);
        wxString relativeFile = fname.GetFullPath();

        wxString destFilename = destinationFolder + wxFILE_SEP_PATH + relativeFile;

        wxString path = wxPathOnly(destFilename);

        if (progressDialog)
        {
            wxString msg(wxString::Format(_("Writing %s"), relativeFile.c_str()));
            if (!progressDialog->Update(i, msg))
            {
                // Aborting on user request
                return false;
            }
        }

        if (!wxDirExists(path))
            FileUtilities::MkDirRecursively(path);

        if (!wxCopyFile(sourceFile, destFilename))
            return false;
    }

    return true;
}



